#include "joystick.h"

#ifndef WIN32
	#include <usb.h>
#endif

Joystick::Joystick()
{
	settings = Settings::Instance();

	if (SDL_Init( SDL_INIT_JOYSTICK ) < 0)
	{
		fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
		exit(1);
	}

	printf("%i joystick(s) found:\n", SDL_NumJoysticks() );

	if ( SDL_NumJoysticks() > 0 )
	{
		for( int i=0; i < SDL_NumJoysticks(); i++ ) 
		{
			printf(" - %s\n", SDL_JoystickName(i));
		}

		SDL_Joystick *joystick;

		SDL_JoystickEventState(SDL_ENABLE);
		joystick = SDL_JoystickOpen(0);
#ifndef WIN32
		// Open event device with write permission
		ff_fd = open(settings->getCVarS("ffdev").c_str(), O_RDWR|O_NONBLOCK);
		if (ff_fd == -1) {
			fprintf(stderr,"ERROR: can not open %s (%s)\n", settings->getCVarS("ffdev").c_str(), strerror(errno));
		}
		else
		{
			std::cout << "setting up force feedback" << std::endl;
			/* Switch off auto centering */
			play.type=EV_FF;
			play.code=FF_AUTOCENTER;
			play.value=0;
			if (write(ff_fd,&play, sizeof(play))!=sizeof(play) ) {
				fprintf(stderr,"ERROR: failed to disable auto centering (%s) [%s:%d]\n",
					strerror(errno),__FILE__,__LINE__);
				exit(1);
			}

			// deadzone = 0
				int axisindex = 0;
				__s32 deadzonevalue = 0;
				uint8_t abs_bitmask[ABS_MAX/8 + 1];
// 				float pourcent_deadzone;
				struct input_absinfo abs_features;
				
				memset(abs_bitmask, 0, sizeof(abs_bitmask));
				if (ioctl(ff_fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) < 0)
				{
					perror("evdev ioctl");
					exit(1);
				}
				
				if(ioctl(ff_fd, EVIOCGABS(axisindex), &abs_features))
				{
					perror("evdev EVIOCGABS ioctl");
					exit(1);
				}

				printf("Setting deadzone value to : %d\n",deadzonevalue);
				abs_features.flat=deadzonevalue;
				if(ioctl(ff_fd, EVIOCSABS(axisindex), &abs_features)) {
					perror("evdev EVIOCSABS ioctl");
					exit(1);
				}
				
				if(ioctl(ff_fd, EVIOCGABS(axisindex), &abs_features))
				{
					perror("evdev EVIOCGABS ioctl");
					exit(1);
				}

			// auto center force
				effect.type = FF_CONSTANT;
				//         effect.u.constant.level = 0x7fff * max(fabs(nx), fabs(ny));
// 				effect.u.constant.level = 0x0000;
				effect.u.constant.level = 0x0;
				effect.direction =  0xa000;
				// printf("level: %04x direction: %04x\n", (unsigned int)effect.u.constant.level, (unsigned int)effect.direction);
				effect.u.constant.envelope.attack_length = 0;
				effect.u.constant.envelope.attack_level = 0;
				effect.u.constant.envelope.fade_length = 0;
				effect.u.constant.envelope.fade_level = 0;
				effect.trigger.button = 0;
				effect.trigger.interval = 0;
				effect.replay.length = 0xffff;
				effect.replay.delay = 0;
				effect.id = -1;
				
				if (ioctl(ff_fd, EVIOCSFF, &effect) < -1) {
					/* If updates are sent to frequently, they can be refused */
				}

				// struct input_event play;
				play.type = EV_FF;
				play.code = effect.id;
				play.value = 1;

				if (write(ff_fd, (const void*) &play, sizeof(play)) == -1)
				{
					perror("Play effect");
					exit(1);
				}
		}
#endif
	}

}

void Joystick::applyForce( float forwardWheelOrientation )
{
#ifndef WIN32
        if ( ff_fd > -1 )
        {
		float maximumx = 150; // <--- FF strength
		float x = maximumx/2 + (forwardWheelOrientation);
		float nx = fabs(2.0*(x-maximumx/2.0)/maximumx);
		
		if (nx > 1.0f)
			nx = 1.0f;

// 		cerr << nx << endl;
		
		effect2.type = FF_CONSTANT;
		effect2.u.constant.level = 0x7fff * nx;

		if ( forwardWheelOrientation < 0 )
			effect2.direction = 0x4000;
		else if ( forwardWheelOrientation > 0 )
			effect2.direction = 0xc000;
		
	// printf("level: %04x direction: %04x\n", (unsigned int)effect.u.constant.level, (unsigned int)effect.direction);
		effect2.u.constant.envelope.attack_length = 0;
		effect2.u.constant.envelope.attack_level = 0x100;
		effect2.u.constant.envelope.fade_length = 0;
		effect2.u.constant.envelope.fade_level = 0;
		effect2.trigger.button = 0;
		effect2.trigger.interval = 0;
		effect2.replay.length = 0xffff;
		effect2.replay.delay = 0;
	//                effect.id = -1;

		if (ioctl(ff_fd, EVIOCSFF, &effect2) < -1) {
			/* If updates are sent to frequently, they can be refused */
		}
	}
#endif
}

// int Joystick::G25send_command(char command[7])
// {
// #ifndef WIN32
// 	int stat;
// 
// 	usb_find_device(VENDOR,G25EXTENDED);
// 	
// 	usb_handle = usb_open( dev );
// 	if (!usb_handle) {
// 		fprintf(stderr,"usb_open: %s\n",strerror(errno));
// 	}
// 	if (verbose_flag) printf("Device opened\n");
// 
// 	stat = usb_detach_kernel_driver_np(usb_handle, 0);
// 	if (stat<0 && verbose_flag) {
// 		fprintf(stderr,"usb_detach_kernel_driver_np: %i %s\n",stat,strerror(-stat));
// 	}
// 	if (verbose_flag) printf ("Kernel driver released\n");
// 
// 	stat = usb_claim_interface(usb_handle, 0);
// 	if (stat<0) {
// 		if ( (stat == -EBUSY) && verbose_flag) fprintf(stderr, "usb_claim_interface: Check that you have permissions to write to %s/%s and, if you don't, that you set up udev correctly.\n", usb_handle->bus->dirname, usb_handle->device->filename);
// 		if ( (stat == -ENOMEM) && verbose_flag) fprintf(stderr, "usb_claim_interface: Insuffisant memory\n");
// 	}
// 	if (verbose_flag) printf ("USB interface claimed\n");
// 
// 	stat=usb_interrupt_write(usb_handle, 1, command, sizeof(command), 100);
// 	if ( (stat < 0) ) { 
// 		if ( (stat == -ENODEV) && verbose_flag) fprintf(stderr, "usb_interrupt_write: No such device, changed identity ?\n");
// 	}
// 	else {
// 		if (verbose_flag) printf ("USB command sent\n");
// 		stat=usb_release_interface(usb_handle, 0);
// 		if (stat<0 && verbose_flag) {
// 			fprintf(stderr,"usb_release_interface: %i %s\n",stat,strerror(-stat));
// 		}
// 		if (verbose_flag) printf ("USB interface released\n");
// 	}
// 
// 	stat=usb_close(usb_handle);
// 	if (stat<0) {
// 		USB_ERROR_STR("usb_close: %s\n",strerror(-stat));
// 	}
// 
// 	stat=usb_rebind_kernel_driver_np();
// 	if (stat<0 && verbose_flag) {
// 		fprintf(stderr,"usb_rebind_kernel_driver_np: %i %s\n",stat,strerror(-stat));
// 	}
// 	if (verbose_flag) printf ("Kernel driver rebinded\n");
// 
// #endif
// 	return 0;
// }
// 
// struct usb_device*  Joystick::usb_find_device(uint16_t vendor,uint16_t product)
// {
//   usb_init();
//   usb_find_busses();
//   usb_find_devices();
// 
//   busses = usb_get_busses();
//   for (bus = busses; bus; bus = bus->next) {
// 
//     for (dev = bus->devices; dev; dev = dev->next) {
//       if (dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product) {
//         return dev;
//       }
//     }
//   }
//   return NULL;
// }

Joystick::~Joystick()
{
}